/**
 * 自定义Socket.io Provider for Yjs
 * 基于现有的Socket.io连接，实现Yjs的同步协议
 */
import * as Y from 'yjs';
import * as awarenessProtocol from 'y-protocols/awareness';
import { Observable } from 'lib0/observable';
import { io, Socket } from 'socket.io-client';

export class SocketIOProvider extends Observable<string> {
  public socket: Socket;
  public roomName: string;
  public doc: Y.Doc;
  public awareness: awarenessProtocol.Awareness | null = null;
  private _synced: boolean = false;
  private _updateHandler: (update: Uint8Array, origin: unknown) => void;
  private _awarenessUpdateHandler: ((update: Uint8Array, origin: unknown) => void) | null = null;
  private _ownsSocket: boolean = false; // 标记是否拥有 Socket 连接（需要清理）
  private _pendingUpdates: Uint8Array[] = []; // 存储断开连接时的待发送更新

  constructor(serverUrl: string | Socket, roomName: string, doc: Y.Doc, options: { awareness?: boolean } = {}) {
    super();
    this.roomName = roomName;
    this.doc = doc;
    
    // 如果传入的是 Socket 实例，复用它；否则创建新连接
    if (typeof serverUrl === 'string') {
      this.socket = io(serverUrl, {
        transports: ['websocket', 'polling'],
        reconnection: true,
        reconnectionAttempts: 5,
        reconnectionDelay: 1000,
        reconnectionDelayMax: 5000,
        timeout: 20000,
        ...options
      });
      this._ownsSocket = true;
    } else {
      this.socket = serverUrl;
      this._ownsSocket = false;
    }
    
    // 监听连接错误，记录详细信息
    this.socket.on('connect_error', (error) => {
      console.error('[Yjs Provider] 连接错误:', error.message);
    });
    
    // 监听重连尝试
    this.socket.on('reconnect_attempt', (attemptNumber) => {
      console.log(`[Yjs Provider] 重连尝试 ${attemptNumber}`);
    });
    
    // 监听重连成功
    this.socket.on('reconnect', (attemptNumber) => {
      console.log(`[Yjs Provider] 重连成功，尝试次数: ${attemptNumber}`);
      sendPendingUpdates();
    });
    
    // 监听重连失败
    this.socket.on('reconnect_failed', () => {
      console.error('[Yjs Provider] 重连失败');
    });

    // 发送待处理的更新
    const sendPendingUpdates = () => {
      if (this._pendingUpdates.length > 0 && this.socket.connected) {
        console.log(`[Yjs Provider] 开始发送 ${this._pendingUpdates.length} 个待处理更新`);
        const pendingUpdatesCopy = [...this._pendingUpdates];
        this._pendingUpdates = []; // 清空队列
        
        pendingUpdatesCopy.forEach((update, index) => {
          try {
            const binaryString = Array.from(update, byte => String.fromCharCode(byte)).join('');
            const base64 = btoa(binaryString);
            this.socket.emit('yjs-update', {
              roomName: this.roomName,
              update: base64
            });
            console.log(`[Yjs Provider] ✅ 发送待处理更新 ${index + 1}/${pendingUpdatesCopy.length}`);
          } catch (error) {
            console.error(`[Yjs Provider] ❌ 发送待处理更新失败 ${index + 1}/${pendingUpdatesCopy.length}:`, error);
            // 如果发送失败，重新加入队列末尾
            this._pendingUpdates.push(update);
          }
        });
        
        // 如果仍有待处理更新，1秒后再次尝试
        if (this._pendingUpdates.length > 0) {
          console.log(`[Yjs Provider] 仍有 ${this._pendingUpdates.length} 个待处理更新，稍后重试`);
          setTimeout(sendPendingUpdates, 1000);
        }
      }
    };

    // 监听连接
    const joinRoom = () => {
      console.log('[Yjs Provider] 加入 Yjs 房间:', this.roomName);
      // 加入房间
      this.socket.emit('yjs-join', { roomName: this.roomName });
      // 连接后立即请求同步
      this.socket.emit('yjs-sync', { roomName: this.roomName });
      // 发送待处理更新
      sendPendingUpdates();
    };
    
    this.socket.on('connect', () => {
      console.log('[Yjs Provider] 已连接到服务器');
      joinRoom();
      // 连接成功后立即发送待处理更新
      sendPendingUpdates();
      // 延迟再次尝试，确保连接稳定后再发送
      setTimeout(sendPendingUpdates, 300);
      setTimeout(sendPendingUpdates, 1000);
    });
    
    // 如果 Socket 已经连接，立即加入房间
    if (this.socket.connected) {
      console.log('[Yjs Provider] Socket 已连接，立即加入房间');
      joinRoom();
    } else {
      console.log('[Yjs Provider] Socket 未连接，等待连接事件');
      // 主动尝试连接（如果是自己创建的socket）
      if (this._ownsSocket) {
        this.socket.connect();
      }
    }

    // 监听断开连接
    this.socket.on('disconnect', (reason) => {
      console.log(`[Yjs Provider] 已断开连接，原因: ${reason}`);
      this._synced = false;
      
      // 处理不同的断开原因
      if (reason === 'io server disconnect') {
        // 服务器主动断开，客户端需要手动重连
        console.log('[Yjs Provider] 服务器主动断开，尝试重连...');
        setTimeout(() => {
          if (this.socket) {
            this.socket.connect();
          }
        }, 1000);
      }
    });

    // 监听Yjs同步消息
    this.socket.on('yjs-sync', (data: { update?: string, state?: string }) => {
      if (data.state) {
        // 处理从后端数据库加载的初始状态
        console.log('[Yjs Provider] 收到文档初始状态');
        const state = Uint8Array.from(atob(data.state), c => c.charCodeAt(0));
        Y.applyUpdate(this.doc, state, this);
        this._synced = true;
        this.emit('synced', [this]);
        this.emit('status', [{ status: 'connected' }]);
        // 同步完成后发送待处理更新，增加多次尝试
        sendPendingUpdates();
        setTimeout(sendPendingUpdates, 100);
        setTimeout(sendPendingUpdates, 500);
      } else if (data.update) {
        // 保持向后兼容，处理旧格式的update数据
        const update = Uint8Array.from(atob(data.update), c => c.charCodeAt(0));
        Y.applyUpdate(this.doc, update, this);
      }
    });

    // 监听Yjs状态更新
    this.socket.on('yjs-ack', () => {
      this._synced = true;
      this.emit('synced', [this]);
      this.emit('status', [{ status: 'connected' }]);
      // 确认同步后立即发送待处理更新
      sendPendingUpdates();
    });

    // 监听Awareness更新
    this.socket.on('yjs-awareness', (data: { update: string }) => {
      if (data.update && this.awareness) {
        try {
          const update = Uint8Array.from(atob(data.update), c => c.charCodeAt(0));
          awarenessProtocol.applyAwarenessUpdate(this.awareness, update, this);
          console.log('[Yjs Provider] 收到并应用 Awareness 更新');
        } catch (error) {
          console.error('[Yjs Provider] 应用 Awareness 更新失败:', error);
        }
      }
    });

    // 监听文档初始状态
    this.socket.on('yjs-init', async (data: { state: string }) => {
      if (data.state) {
        const state = Uint8Array.from(atob(data.state), c => c.charCodeAt(0));
        Y.applyUpdate(this.doc, state, this);
        this._synced = true;
        this.emit('synced', [this]);
        this.emit('status', [{ status: 'connected' }]);
        // 初始化完成后立即发送待处理更新
        sendPendingUpdates();
      }
    });

    // 监听文档更新
    this.socket.on('yjs-update', (data: { update: string }) => {
      if (data.update) {
        try {
          const update = Uint8Array.from(atob(data.update), c => c.charCodeAt(0));
          Y.applyUpdate(this.doc, update, this);
          console.log('[Yjs Provider] 收到并应用远程更新');
        } catch (error) {
          console.error('[Yjs Provider] 应用更新失败:', error);
        }
      }
    });

    // 设置文档更新监听器
    // 注意：origin !== this 确保不会将接收到的更新再次发送回去
    // QuillBinding 会以其他 origin 发送更新，这些需要发送
    this._updateHandler = (update: Uint8Array, origin: unknown) => {
      // 只有当更新不是来自 provider 本身（即不是从服务器接收的）时才发送
      // QuillBinding 会以 'quill' 或其他 origin 发送更新，这些需要发送
      // 注意：origin 可能是 QuillBinding 实例或其他对象，只要不是 this 就应该发送
      
      // 记录 origin 信息以便调试
      const originName = origin && typeof origin === 'object' && 'constructor' in origin 
        ? (origin as { constructor: { name: string } }).constructor.name 
        : String(origin);
      
      console.log('[Yjs Provider] 收到 Yjs 更新事件, origin:', originName, 'update size:', update.length, 'connected:', this.socket.connected);
      
      // 过滤掉来自服务器的更新，不需要再次发送
      if (origin === this) {
        console.log('[Yjs Provider] ⚠️ 跳过发送（来自服务器，origin === this）');
        return;
      }
      
      // 尝试发送更新
      if (this.socket.connected) {
        try {
          // 将更新发送到服务器
          // 使用更安全的方法将 Uint8Array 转换为 base64
          // 避免使用扩展语法，因为大数组会导致错误
          const binaryString = Array.from(update, byte => String.fromCharCode(byte)).join('');
          const base64 = btoa(binaryString);
          this.socket.emit('yjs-update', {
            roomName: this.roomName,
            update: base64
          });
          console.log('[Yjs Provider] ✅ 发送更新到服务器, origin:', originName, 'update size:', update.length);
        } catch (error) {
          console.error('[Yjs Provider] ❌ 发送更新失败:', error);
          // 如果发送失败，添加到待处理队列
          this._pendingUpdates.push(update);
          console.log('[Yjs Provider] 添加发送失败的更新到待处理队列，当前队列大小:', this._pendingUpdates.length);
        }
      } else {
        // 连接断开时，将更新添加到待处理队列
        console.log('[Yjs Provider] ⚠️ 连接未建立，更新已添加到待处理队列，当前队列大小:', this._pendingUpdates.length);
        this._pendingUpdates.push(update);
        
        // 限制待处理队列大小，防止内存溢出
        if (this._pendingUpdates.length > 100) {
          console.warn('[Yjs Provider] 待处理队列超过限制，移除最早的更新');
          this._pendingUpdates = this._pendingUpdates.slice(-100); // 保留最新的100个更新
        }
      }
    };

    this.doc.on('update', this._updateHandler);

    // 初始化Awareness（用于显示其他用户的光标位置）
    if (options.awareness !== false) {
      this.awareness = new awarenessProtocol.Awareness(this.doc);
      
      this._awarenessUpdateHandler = (update: Uint8Array, origin: unknown) => {
        if (origin !== this && this.socket.connected) {
          try {
            // 使用更安全的方法将 Uint8Array 转换为 base64
            // 避免使用扩展语法，因为大数组会导致错误
            const binaryString = Array.from(update, byte => String.fromCharCode(byte)).join('');
            const base64 = btoa(binaryString);
            this.socket.emit('yjs-awareness', {
              roomName: this.roomName,
              update: base64
            });
            console.log('[Yjs Provider] 发送 Awareness 更新到服务器, size:', update.length);
          } catch (error) {
            console.error('[Yjs Provider] 发送 Awareness 更新失败:', error);
          }
        }
      };
      
      this.awareness.on('update', this._awarenessUpdateHandler);
      
      // 定期发送 Awareness 更新（确保其他用户能看到）
      // 注意：Awareness 的 update 事件已经会触发发送，这里作为备用
      let awarenessInterval: NodeJS.Timeout | null = null;
      
      const sendAwarenessUpdate = () => {
        if (this.socket.connected && this.awareness) {
          try {
            const update = awarenessProtocol.encodeAwarenessUpdate(
              this.awareness,
              Array.from(this.awareness.getStates().keys())
            );
            if (update.length > 0) {
              // 使用更安全的方法将 Uint8Array 转换为 base64
              const binaryString = Array.from(update, byte => String.fromCharCode(byte)).join('');
              const base64 = btoa(binaryString);
              this.socket.emit('yjs-awareness', {
                roomName: this.roomName,
                update: base64
              });
            }
          } catch (error) {
            console.error('[Yjs Provider] 发送 Awareness 更新失败:', error);
          }
        }
      };
      
      // 每5秒发送一次（作为心跳，确保连接正常）
      awarenessInterval = setInterval(sendAwarenessUpdate, 5000);
      
      // 清理定时器
      this.socket.on('disconnect', () => {
        if (awarenessInterval) {
          clearInterval(awarenessInterval);
          awarenessInterval = null;
        }
      });
      
      console.log('[Yjs Provider] Awareness 已初始化');
    }

    // 如果已经连接，立即请求同步；否则等待 connect 事件
    if (this.socket.connected) {
      this.socket.emit('yjs-join', { roomName: this.roomName });
      this.socket.emit('yjs-sync', { roomName: this.roomName });
    }
  }

  public destroy(): void {
    if (this._updateHandler) {
      this.doc.off('update', this._updateHandler);
    }
    if (this._awarenessUpdateHandler && this.awareness) {
      this.awareness.off('update', this._awarenessUpdateHandler);
    }
    if (this.awareness) {
      this.awareness.destroy();
    }
    this.socket.emit('yjs-leave', { roomName: this.roomName });
    // 只有拥有 Socket 连接时才断开
    if (this._ownsSocket) {
      this.socket.disconnect();
    }
    super.destroy();
  }

  public get synced(): boolean {
    return this._synced;
  }
}

